﻿using HelixToolkit.Wpf.SharpDX;
using HelixToolkit.Wpf.SharpDX.Model.Scene;
using SharpDX;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media.Media3D;

namespace X.WPF.Controls.Extends
{

    public static class HelixToolkitExtends
    {
        #region 顶视图 —— static void TopView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 顶视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void TopView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), animationTime);
            }

        }
        #endregion

        #region 底视图 —— static void BottomView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 底视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void BottomView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 0, 1), new Vector3D(0, -1, 0), animationTime);
            }
        }
        #endregion

        #region 前视图 —— static void FrontView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 前视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void FrontView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1), animationTime);
            }
        }
        #endregion

        #region 后视图 —— static void BackView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 后视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void BackView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 0, 1), new Vector3D(0, 1, 0), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(1, 0, 0), new Vector3D(0, 0, 1), animationTime);
            }
        }
        #endregion

        #region 左视图 —— static void LeftView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 左视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void LeftView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(1, 0, 0), new Vector3D(0, 1, 0), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), animationTime);
            }
        }
        #endregion

        #region 右视图 —— static void RightView(this Viewport3DX viewport3Dx, double animationTime = 0)
        /// <summary>
        /// 右视图
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="animationTime">动画时间</param>
        public static void RightView(this Viewport3DX viewport3Dx, double animationTime = 0)
        {
            if (viewport3Dx.IsModelUpDirectionY())
            {
                viewport3Dx.ChangeDirection(new Vector3D(-1, 0, 0), new Vector3D(0, 1, 0), animationTime);
            }
            else
            {
                viewport3Dx.ChangeDirection(new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), animationTime);
            }
        }
        #endregion

        #region 最近位置 —— static Point3D? NearestHit(this IEnumerable<HitTestResult> hitResults)
        /// <summary>
        /// 最近位置
        /// </summary>
        /// <param name="hitResults">命中测试结果列表</param>
        /// <returns>最近位置</returns>
        public static Point3D? Nearest(this IEnumerable<HitTestResult> hitResults)
        {
            HitTestResult nearestHit = hitResults.OrderBy(h => h.Distance).FirstOrDefault();

            Point3D? result = nearestHit?.PointHit.ToPoint3D();

            return result;
        }
        #endregion

        #region 最远位置 —— static Point3D? Farthest(this IEnumerable<HitTestResult> hitResults)
        /// <summary>
        /// 最远位置
        /// </summary>
        /// <param name="hitResults">命中测试结果列表</param> 
        /// <returns>最远位置</returns>
        public static Point3D? Farthest(this IEnumerable<HitTestResult> hitResults)
        {
            HitTestResult nearestHit = hitResults.OrderByDescending(h => h.Distance).FirstOrDefault();

            Point3D? result = nearestHit?.PointHit.ToPoint3D();

            return result;
        }
        #endregion

        #region 命中测试 —— static void HitTest(this Viewport3DX viewport3Dx, SceneNode sceneNode...
        /// <summary>
        /// 命中测试
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="sceneNode">节点</param>
        /// <param name="location">3D坐标</param>
        /// <param name="hitResults">命中测试结果列表</param>
        /// <remarks>射线方向：X轴正负、Y轴正负、Z轴正负</remarks>
        public static void HitTest(this Viewport3DX viewport3Dx, SceneNode sceneNode, Point3D location, ref List<HitTestResult> hitResults)
        {
            Vector3 direction = new Vector3(1, 0, 0);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);

            direction = new Vector3(-1, 0, 0);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);

            direction = new Vector3(0, 1, 0);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);

            direction = new Vector3(0, -1, 0);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);

            direction = new Vector3(0, 0, 1);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);

            direction = new Vector3(0, 0, -1);
            viewport3Dx.HitTest(sceneNode, location, direction, ref hitResults);
        }
        #endregion

        #region 命中测试 —— static void HitTest(this Viewport3DX viewport3Dx, SceneNode sceneNode...
        /// <summary>
        /// 命中测试
        /// </summary>
        /// <param name="viewport3Dx">3D组件</param>
        /// <param name="sceneNode">节点</param>
        /// <param name="location">3D位置</param>
        /// <param name="rayDirection">射线方向</param>
        /// <param name="hitResults">命中测试结果列表</param>
        public static void HitTest(this Viewport3DX viewport3Dx, SceneNode sceneNode, Point3D location, Vector3 rayDirection, ref List<HitTestResult> hitResults)
        {
            if (sceneNode == null)
                return;

            rayDirection.Normalize();
            Ray ray = new Ray(location.ToVector3(), rayDirection);

            HitTestContext hitTestContext = new HitTestContext(viewport3Dx.RenderHost.RenderContext, ref ray);
            sceneNode?.HitTest(hitTestContext, ref hitResults);

        }
        #endregion

        #region Point3D映射 —— static void HitTest(this Viewport3DX viewport3Dx, SceneNode sceneNode...
        /// <summary>
        /// Point3D映射
        /// </summary>
        /// <param name="source">3D点</param>
        public static TranslateTransform3D ToTranslateTransform3D(this Point3D source)
        {
            TranslateTransform3D result = new TranslateTransform3D(source.X, source.Y, source.Z);
            return result;
        }
        #endregion
    }
}
